<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-picker-Month'>/**
</span> * @private
 * @class Ext.picker.Month
 * @extends Ext.Component
 * &lt;p&gt;A month picker component. This class is used by the {@link Ext.picker.Date DatePicker} class
 * to allow browsing and selection of year/months combinations.&lt;/p&gt;
 */
Ext.define('Ext.picker.Month', {
    extend: 'Ext.Component',
    requires: ['Ext.XTemplate', 'Ext.util.ClickRepeater', 'Ext.Date', 'Ext.button.Button'],
    alias: 'widget.monthpicker',
    alternateClassName: 'Ext.MonthPicker',

    renderTpl: [
        '&lt;div class=&quot;{baseCls}-body&quot;&gt;',
          '&lt;div class=&quot;{baseCls}-months&quot;&gt;',
              '&lt;tpl for=&quot;months&quot;&gt;',
                  '&lt;div class=&quot;{parent.baseCls}-item {parent.baseCls}-month&quot;&gt;&lt;a href=&quot;#&quot; hidefocus=&quot;on&quot;&gt;{.}&lt;/a&gt;&lt;/div&gt;',
              '&lt;/tpl&gt;',
          '&lt;/div&gt;',
          '&lt;div class=&quot;{baseCls}-years&quot;&gt;',
              '&lt;div class=&quot;{baseCls}-yearnav&quot;&gt;',
                  '&lt;button class=&quot;{baseCls}-yearnav-prev&quot;&gt;&lt;/button&gt;',
                  '&lt;button class=&quot;{baseCls}-yearnav-next&quot;&gt;&lt;/button&gt;',
              '&lt;/div&gt;',
              '&lt;tpl for=&quot;years&quot;&gt;',
                  '&lt;div class=&quot;{parent.baseCls}-item {parent.baseCls}-year&quot;&gt;&lt;a href=&quot;#&quot; hidefocus=&quot;on&quot;&gt;{.}&lt;/a&gt;&lt;/div&gt;',
              '&lt;/tpl&gt;',
          '&lt;/div&gt;',
        '&lt;/div&gt;',
        '&lt;div class=&quot;' + Ext.baseCSSPrefix + 'clear&quot;&gt;&lt;/div&gt;',
        '&lt;tpl if=&quot;showButtons&quot;&gt;',
          '&lt;div class=&quot;{baseCls}-buttons&quot;&gt;&lt;/div&gt;',
        '&lt;/tpl&gt;'
    ],

<span id='Ext-picker-Month-cfg-okText'>    /**
</span>     * @cfg {String} okText The text to display on the ok button. Defaults to &lt;tt&gt;'OK'&lt;/tt&gt;
     */
    okText: 'OK',

<span id='Ext-picker-Month-cfg-cancelText'>    /**
</span>     * @cfg {String} cancelText The text to display on the cancel button. Defaults to &lt;tt&gt;'Cancel'&lt;/tt&gt;
     */
    cancelText: 'Cancel',

<span id='Ext-picker-Month-cfg-baseCls'>    /**
</span>     * @cfg {String} baseCls The base CSS class to apply to the picker element. Defaults to &lt;tt&gt;'x-monthpicker'&lt;/tt&gt;
     */
    baseCls: Ext.baseCSSPrefix + 'monthpicker',

<span id='Ext-picker-Month-cfg-showButtons'>    /**
</span>     * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker. Defaults to &lt;tt&gt;true&lt;/tt&gt;.
     */
    showButtons: true,

<span id='Ext-picker-Month-cfg-selectedCls'>    /**
</span>     * @cfg {String} selectedCls The class to be added to selected items in the picker. Defaults to
     * &lt;tt&gt;'x-monthpicker-selected'&lt;/tt&gt;
     */

<span id='Ext-picker-Month-cfg-value'>    /**
</span>     * @cfg {Date/Array} value The default value to set. See {#setValue setValue}
     */
    width: 178,
    
    // used when attached to date picker which isnt showing buttons
    smallCls: Ext.baseCSSPrefix + 'monthpicker-small',

    // private
    totalYears: 10,
    yearOffset: 5, // 10 years in total, 2 per row
    monthOffset: 6, // 12 months, 2 per row

    // private, inherit docs
    initComponent: function(){
        var me = this;

        me.selectedCls = me.baseCls + '-selected';
        me.addEvents(
<span id='Ext-picker-Month-event-cancelclick'>            /**
</span>             * @event cancelclick
             * Fires when the cancel button is pressed.
             * @param {Ext.picker.Month} this
             */
            'cancelclick',

<span id='Ext-picker-Month-event-monthclick'>            /**
</span>             * @event monthclick
             * Fires when a month is clicked.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'monthclick',

<span id='Ext-picker-Month-event-monthdblclick'>            /**
</span>             * @event monthdblclick
             * Fires when a month is clicked.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'monthdblclick',

<span id='Ext-picker-Month-event-okclick'>            /**
</span>             * @event okclick
             * Fires when the ok button is pressed.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'okclick',

<span id='Ext-picker-Month-event-select'>            /**
</span>             * @event select
             * Fires when a month/year is selected.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'select',

<span id='Ext-picker-Month-event-yearclick'>            /**
</span>             * @event yearclick
             * Fires when a year is clicked.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'yearclick',

<span id='Ext-picker-Month-event-yeardblclick'>            /**
</span>             * @event yeardblclick
             * Fires when a year is clicked.
             * @param {Ext.picker.Month} this
             * @param {Array} value The current value
             */
            'yeardblclick'
        );
        if (me.small) {
            me.addCls(me.smallCls);
        }
        me.setValue(me.value);
        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
        this.callParent();
    },

    // private, inherit docs
    onRender: function(ct, position){
        var me = this,
            i = 0,
            months = [],
            shortName = Ext.Date.getShortMonthName,
            monthLen = me.monthOffset;

        for (; i &lt; monthLen; ++i) {
            months.push(shortName(i), shortName(i + monthLen));
        }

        Ext.apply(me.renderData, {
            months: months,
            years: me.getYears(),
            showButtons: me.showButtons
        });

        Ext.apply(me.renderSelectors, {
            bodyEl: '.' + me.baseCls + '-body',
            prevEl: '.' + me.baseCls + '-yearnav-prev',
            nextEl: '.' + me.baseCls + '-yearnav-next',
            buttonsEl: '.' + me.baseCls + '-buttons'
        });
        this.callParent([ct, position]);
    },

    // private, inherit docs
    afterRender: function(){
        var me = this,
            body = me.bodyEl,
            buttonsEl = me.buttonsEl;

        me.callParent();

        me.mon(body, 'click', me.onBodyClick, me);
        me.mon(body, 'dblclick', me.onBodyClick, me);

        // keep a reference to the year/month elements since we'll be re-using them
        me.years = body.select('.' + me.baseCls + '-year a');
        me.months = body.select('.' + me.baseCls + '-month a');

        if (me.showButtons) {
            me.okBtn = Ext.create('Ext.button.Button', {
                text: me.okText,
                renderTo: buttonsEl,
                handler: me.onOkClick,
                scope: me
            });
            me.cancelBtn = Ext.create('Ext.button.Button', {
                text: me.cancelText,
                renderTo: buttonsEl,
                handler: me.onCancelClick,
                scope: me
            });
        }

        me.backRepeater = Ext.create('Ext.util.ClickRepeater', me.prevEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
        });

        me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
        me.nextRepeater = Ext.create('Ext.util.ClickRepeater', me.nextEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
        });
        me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
        me.updateBody();
    },

<span id='Ext-picker-Month-method-setValue'>    /**
</span>     * Set the value for the picker.
     * @param {Date/Array} value The value to set. It can be a Date object, where the month/year will be extracted, or
     * it can be an array, with the month as the first index and the year as the second.
     * @return {Ext.picker.Month} this
     */
    setValue: function(value){
        var me = this,
            active = me.activeYear,
            offset = me.monthOffset,
            year,
            index;

        if (!value) {
            me.value = [null, null];
        } else if (Ext.isDate(value)) {
            me.value = [value.getMonth(), value.getFullYear()];
        } else {
            me.value = [value[0], value[1]];
        }

        if (me.rendered) {
            year = me.value[1];
            if (year !== null) {
                if ((year &lt; active || year &gt; active + me.yearOffset)) {
                    me.activeYear = year - me.yearOffset + 1;
                }
            }
            me.updateBody();
        }

        return me;
    },

<span id='Ext-picker-Month-method-getValue'>    /**
</span>     * Gets the selected value. It is returned as an array [month, year]. It may
     * be a partial value, for example [null, 2010]. The month is returned as
     * 0 based.
     * @return {Array} The selected value
     */
    getValue: function(){
        return this.value;
    },

<span id='Ext-picker-Month-method-hasSelection'>    /**
</span>     * Checks whether the picker has a selection
     * @return {Boolean} Returns true if both a month and year have been selected
     */
    hasSelection: function(){
        var value = this.value;
        return value[0] !== null &amp;&amp; value[1] !== null;
    },

<span id='Ext-picker-Month-method-getYears'>    /**
</span>     * Get an array of years to be pushed in the template. It is not in strict
     * numerical order because we want to show them in columns.
     * @private
     * @return {Array} An array of years
     */
    getYears: function(){
        var me = this,
            offset = me.yearOffset,
            start = me.activeYear, // put the &quot;active&quot; year on the left
            end = start + offset,
            i = start,
            years = [];

        for (; i &lt; end; ++i) {
            years.push(i, i + offset);
        }

        return years;
    },

<span id='Ext-picker-Month-method-updateBody'>    /**
</span>     * Update the years in the body based on any change
     * @private
     */
    updateBody: function(){
        var me = this,
            years = me.years,
            months = me.months,
            yearNumbers = me.getYears(),
            cls = me.selectedCls,
            value = me.getYear(null),
            month = me.value[0],
            monthOffset = me.monthOffset,
            year;

        if (me.rendered) {
            years.removeCls(cls);
            months.removeCls(cls);
            years.each(function(el, all, index){
                year = yearNumbers[index];
                el.dom.innerHTML = year;
                if (year == value) {
                    el.dom.className = cls;
                }
            });
            if (month !== null) {
                if (month &lt; monthOffset) {
                    month = month * 2;
                } else {
                    month = (month - monthOffset) * 2 + 1;
                }
                months.item(month).addCls(cls);
            }
        }
    },

<span id='Ext-picker-Month-method-getYear'>    /**
</span>     * Gets the current year value, or the default.
     * @private
     * @param {Number} defaultValue The default value to use if the year is not defined.
     * @param {Number} offset A number to offset the value by
     * @return {Number} The year value
     */
    getYear: function(defaultValue, offset) {
        var year = this.value[1];
        offset = offset || 0;
        return year === null ? defaultValue : year + offset;
    },

<span id='Ext-picker-Month-method-onBodyClick'>    /**
</span>     * React to clicks on the body
     * @private
     */
    onBodyClick: function(e, t) {
        var me = this,
            isDouble = e.type == 'dblclick';

        if (e.getTarget('.' + me.baseCls + '-month')) {
            e.stopEvent();
            me.onMonthClick(t, isDouble);
        } else if (e.getTarget('.' + me.baseCls + '-year')) {
            e.stopEvent();
            me.onYearClick(t, isDouble);
        }
    },

<span id='Ext-picker-Month-method-adjustYear'>    /**
</span>     * Modify the year display by passing an offset.
     * @param {Number} offset The offset to move by. If not specified, it defaults to 10.
     */
    adjustYear: function(offset){
        if (typeof offset != 'number') {
            offset = this.totalYears;
        }
        this.activeYear += offset;
        this.updateBody();
    },

<span id='Ext-picker-Month-method-onOkClick'>    /**
</span>     * React to the ok button being pressed
     * @private
     */
    onOkClick: function(){
        this.fireEvent('okclick', this, this.value);
    },

<span id='Ext-picker-Month-method-onCancelClick'>    /**
</span>     * React to the cancel button being pressed
     * @private
     */
    onCancelClick: function(){
        this.fireEvent('cancelclick', this);
    },

<span id='Ext-picker-Month-method-onMonthClick'>    /**
</span>     * React to a month being clicked
     * @private
     * @param {HTMLElement} target The element that was clicked
     * @param {Boolean} isDouble True if the event was a doubleclick
     */
    onMonthClick: function(target, isDouble){
        var me = this;
        me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
        me.updateBody();
        me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },

<span id='Ext-picker-Month-method-onYearClick'>    /**
</span>     * React to a year being clicked
     * @private
     * @param {HTMLElement} target The element that was clicked
     * @param {Boolean} isDouble True if the event was a doubleclick
     */
    onYearClick: function(target, isDouble){
        var me = this;
        me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
        me.updateBody();
        me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);

    },

<span id='Ext-picker-Month-method-resolveOffset'>    /**
</span>     * Returns an offsetted number based on the position in the collection. Since our collections aren't
     * numerically ordered, this function helps to normalize those differences.
     * @private
     * @param {Object} index
     * @param {Object} offset
     * @return {Number} The correctly offsetted number
     */
    resolveOffset: function(index, offset){
        if (index % 2 === 0) {
            return (index / 2);
        } else {
            return offset + Math.floor(index / 2);
        }
    },

    // private, inherit docs
    beforeDestroy: function(){
        var me = this;
        me.years = me.months = null;
        Ext.destroyMembers('backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
        this.callParent();
    }
});
</pre>
</body>
</html>
